package com.test.utils.parallel;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Playwright;

public class ThreadSafeDriverManager {
    private static final Logger logger = LoggerFactory.getLogger(ThreadSafeDriverManager.class);

    private static final ThreadLocal<WebDriver> webDriverThreadLocal = new ThreadLocal<>();
    private static final ThreadLocal<Playwright> playwrightThreadLocal = new ThreadLocal<>();
    private static final ThreadLocal<Browser> browserThreadLocal = new ThreadLocal<>();
    private static final ThreadLocal<BrowserContext> contextThreadLocal = new ThreadLocal<>();
    private static final ThreadLocal<Page> pageThreadLocal = new ThreadLocal<>();

    private static final ConcurrentHashMap<Long, String> activeDrivers = new ConcurrentHashMap<>();

    // Configuration
    private static String seleniumHubUrl = System.getProperty("selenium.hub.url", "http://localhost:4444/wd/hub");
    private static boolean useRemoteDriver = Boolean.parseBoolean(System.getProperty("use.remote.driver", "false"));
    private static boolean headless = Boolean.parseBoolean(System.getProperty("headless", "true"));

    public static WebDriver getWebDriver() {
        WebDriver driver = webDriverThreadLocal.get();

        if (driver == null) {
            driver = createWebDriver();
            webDriverThreadLocal.set(driver);
            activeDrivers.put(Thread.currentThread().threadId(), "WebDriver");
            logger.info("[Thread-{}] Created new WebDriver instance", Thread.currentThread().threadId());

        }
        return driver;
    }

    public static Page getPlaywrightPage() {
        Page page = pageThreadLocal.get();

        if (page == null) {
            initializePlaywright();
            page = pageThreadLocal.get();
            activeDrivers.put(Thread.currentThread().threadId(), "Playwright");
            logger.info("[Thread-{}] Created new Playwright instance", Thread.currentThread().threadId());
        }

        return page;
    }

    private static WebDriver creatWebDriver() {
        ChromeOptions options = new ChromeOptions();

        options.addArguments("--no-sandbox");
        options.addArguments("--disable-dev-shm-usage");
        options.addArguments("--disable-gpu");
        options.addArguments("window-size=1920,1080");

        if (headless) {
            options.addArguments("--headless");
        }

        options.addArguments("--disable-setuid-sandbox");
        options.addArguments("--remote-debugging-port=9222");
        options.addArguments("disable-extensions");
        options.addArguments("proxy-server='direct://'");
        options.addArguments("--proxy-bypass-list=*");

        WebDriver driver;

        if (useRemoteDriver) {
            try {
                driver = new RemoteWebDriver(new URL(seleniumHubUrl), options);
                logger.info("Created RemoteWebDriver connected to: {}", seleniumHubUrl);
            } catch (MalformedURLException e) {
                throw new RuntimeException("Invalid Selenium Hub URL: " + seleniumHubUrl, e);
            }
        } else {
            driver = new ChromeDriver(options);
            logger.info("Created local ChromeDriver");
        }

        driver.manage().window().maximize();
        return driver;
    }

    private static void initializePlaywright() {
        Playwright playwright = Playwright.create();
        playwrightThreadLocal.set(playwright);

        BrowserType.LaunchOptions launchOptions = new BrowserType.LaunchOptions()
            .setHeadless(headless)
            .setArgs(java.util.Arrays.asList(
                "--no-sandbox",
                "--disable-setuid-sandbox",
                "--disable-dev-shm-usage",
                "--disable-gpu"
            ));
        
        Browser browser = playwright.chromium().launch(launchOptions);
        browserThreadLocal.set(browser);

        java.nio.file.Path videosDir = java.nio.file.Paths.get("target/videos");
        try {
            java.nio.file.Files.createDirectories(videosDir);
        } catch (Exception e) {
            logger.error("Failed to create videos directory: {}", e.getMessage());
        }

        Browser.NewContextOptions contextOptions = new Browser.NewContextOptions()
            .setViewportSize(1920, 1080)
            .setIgnoreHTTPSErrors(true)
            .setRecordVideoDir(videosDir);

        BrowserContext context = browser.newContext(contextOptions);
        contextThreadLocal.set(context);

        Page page = context.newPage();
        pageThreadLocal.set(page);

        logger.info("[Thread-{}] Video recording enabled. Videos will be saved to: {}",
            Thread.currentThread().threadId(), videosDir.toAbsolutePath());
    }

    public static void closeWebDriver() {
        WebDriver driver = webDriverThreadLocal.get();

        if (driver != null) {
            try {
                driver.quit();
                logger.info("[Thread-{}] Closed WebDriver instance", Thread.currentThread().threadId());
            } catch (Exception e) {
                logger.error("Error closing WebDriver", e);
            } finally {
                webDriverThreadLocal.remove();
                activeDrivers.remove(Thread.currentThread().threadId());
            }
        }
    }

    public static void closePlaywright() {
        Page page = pageThreadLocal.get();
        BrowserContext context = contextThreadLocal.get();
        Browser browser = browserThreadLocal.get();
        Playwright playwright = playwrightThreadLocal.get();

        if (page != null) {
            try {
                page.clock();
                pageThreadLocal.remove();
            } catch (Exception e) {
                logger.error("Error closing Playwright page", e);
            }
        }

        if (context != null) {
            try {
                context.close();
                contextThreadLocal.remove();
            } catch (Exception e) {
                logger.error("Error closing Playwright context", e);
            }
        }

        if (browser != null) {
            try {
                browser.close();
                browserThreadLocal.remove();
            } catch (Exception e) {
                logger.error("Error closing Playwright browser", e);
            }
        }

        if (playwright != null) {
            try {
                playwright.close();
                playwrightThreadLocal.remove();
                logger.info("[Thread-{}] Closed Playwright instance", Thread.currentThread().threadId());
            } catch (Exception e) {
                logger.error("Error closing Playwright", e);
            } finally {
                activeDrivers.remove(Thread.currentThread().threadId());
            }
        }
    }

    public static void cleanup() {
        closeWebDriver();
        closePlaywright();
    }

    public static void cleanupAll() {
        logger.warn("Cleaning up all active browser sessions");
        activeDrivers.forEach((threadId, type) -> {
            logger.warn("Force closing {} session for thread {}", type, threadId);
        });
    }

    public static int getActiveSessionCount() {
        return activeDrivers.size();
    }

    public static boolean hasActiveDriver() {
        return webDriverThreadLocal.get() != null || pageThreadLocal.get() != null;
    }
}
